/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.dusty.lab2.read;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import ru.dusty.lab2.raid.utils.RaidConfig;
import ru.dusty.lab2.raid.utils.Utils;

/**
 *
 * @author Dusty
 */
public class RaidRead {
    
    private static final String NAME = "read";
    
    private RaidConfig config;
    
    private String root;
    
    private byte[] readRaid(int diskNumber, int block) throws FileNotFoundException, IOException {
        RandomAccessFile raf = new RandomAccessFile(
                new File(root + File.separator + "disk" + diskNumber), "r");
        raf.seek(block * config.getBlockSize());
        short dataSize = raf.readShort();
        byte[] bytes = new byte[dataSize];
        raf.read(bytes);
        raf.close();
        
        return bytes;
    }
    
    public void readRaid0(int block, RandomAccessFile rafFile) throws FileNotFoundException, IOException {  
        
        for (int i = 0; i < config.getDisksCount(); i++) {
            rafFile.write(readRaid(i, block));
        }
        
        rafFile.close();
    }
    
    public void readRaid1(int block, RandomAccessFile rafFile) throws FileNotFoundException, IOException {
        int diskNumber = block / config.getBlocksCount();
        int offsetBlock = block % config.getBlocksCount();
        
        rafFile.write(readRaid(diskNumber, offsetBlock));
        rafFile.close();
    }
    
    public void readRaid3(int block, RandomAccessFile rafFile) throws FileNotFoundException, IOException {
        List<byte[]> list = new ArrayList<byte[]>();
        int fileLength = 0;
        for (int i = 0; i < config.getDisksCount() - 1; i++) {
            byte[] bytes = readRaid(i, block);
            fileLength += bytes.length;
            list.add(bytes);
        }

        byte[] fileData = new byte[fileLength];
        int maxLength = list.get(0).length;
        int index = 0;
        for (int i = 0; i < maxLength; i++) {
            for (byte[] bytes : list) {
                if (i < bytes.length) {
                    fileData[index] = bytes[i];
                    index++;
                }
            }
        }
        rafFile.write(fileData);

        rafFile.close();
    }
    
    public void readRaid4(int block, RandomAccessFile rafFile) throws FileNotFoundException, IOException {
        for (int i = 0; i < config.getDisksCount() - 1; i++) {
            byte[] bytes = readRaid(i, block);
            rafFile.write(bytes);
        }
        
        rafFile.close();
    }
    
    public void readRaid5(int block, RandomAccessFile rafFile) throws FileNotFoundException, IOException {
        int parityDisk = config.getDisksCount() - (block % config.getDisksCount()) - 1;
        for (int i = 0; i < config.getDisksCount(); i++) {
            if (i == parityDisk) continue;
            
            byte[] bytes = readRaid(i, block);
            rafFile.write(bytes);
        }
        
        rafFile.close();
    }
    
    public int read(String[] args) {
        
        if (args.length < 3) {
            System.out.println(NAME + ": missing operand after \"" + NAME + "\"");
            System.out.println(NAME + ": use \"" + NAME + " root N file\"");
            return 1;
        }
        
        root = args[0];
        config = new RaidConfig(root);
        
        try {
            config.loadConfig();
        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": Can not find file with the settings.");
            return 2;
        } catch (IOException ex) {
            System.out.println(NAME + ": Error reading settings file.");
            return 2;
        } catch (NumberFormatException ex) {
            System.out.println(NAME + ": Invalid file format settings.");
            return 2;
        }
        
        if (!config.checkConfig()) return 2;
        
        int block;
        try {
            block = Integer.parseInt(args[1]);
        } catch (NumberFormatException ex) {
            System.out.println(NAME + ": Error parsing number of block.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 3;
        }
        
        String filePath = args[2];
        
        File file = new File(filePath);
        RandomAccessFile rafFile;
        try {
            rafFile = new RandomAccessFile(file, "rw");
        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": " + ex.getMessage());
            return 7;
        }
        
        System.out.println(NAME + ": Reading...");
        
        try {
            switch (config.getRaidLevel()) {
                case 0:
                    if (block < 0 || block >= config.getBlocksCount()) {
                        System.out.println(NAME + ": Wrong number of block.");
                        return 4;
                    }
                    
                    readRaid0(block, rafFile);
                break;
                    
                case 1:
                    if (block < 0 || block >= config.getBlocksCount() * config.getDisksCount()) {
                        System.out.println(NAME + ": Wrong number of block.");
                        return 4;
                    }
                    
                    readRaid1(block, rafFile);
                break;
                    
                case 3:
                    if (!Utils.checkRaid(root, config)) {
                        System.out.println(NAME + ": Some disks are not available or can not be read.");
                        System.out.println(NAME + ": Try rebuild raid.");
                        return 8;
                    }
                    
                    if (block < 0 || block >= config.getBlocksCount()) {
                        System.out.println(NAME + ": Wrong number of block.");
                        return 4;
                    }
                    
                    readRaid3(block, rafFile);
                break;
                    
                case 4:
                    if (!Utils.checkRaid(root, config)) {
                        System.out.println(NAME + ": Some disks are not available or can not be read.");
                        System.out.println(NAME + ": Try rebuild raid.");
                        return 8;
                    }
                    
                    if (block < 0 || block >= config.getBlocksCount()) {
                        System.out.println(NAME + ": Wrong number of block.");
                        return 4;
                    }

                    readRaid4(block, rafFile);
                break;
                    
                case 5:
                    if (!Utils.checkRaid(root, config)) {
                        System.out.println(NAME + ": Some disks are not available or can not be read.");
                        System.out.println(NAME + ": Try rebuild raid.");
                        return 8;
                    }
                    
                    if (block < 0 || block >= config.getBlocksCount()) {
                        System.out.println(NAME + ": Wrong number of block.");
                        return 4;
                    }

                    readRaid5(block, rafFile);
                break;
                    
                default: 
                    System.out.println(NAME + ": RAID level is not supported.");
                    return 8;
            }
        } catch (FileNotFoundException ex) {
            System.out.println(NAME + ": Could not find the file.");   
            System.out.println(NAME + ": " + ex.getMessage());
            System.out.println(NAME + ": Try rebuild raid.");
            return 5;
        } catch (IOException ex) {
            System.out.println(NAME + ": Error occurred while reading the file.");
            System.out.println(NAME + ": " + ex.getMessage());
            return 6;
        }
        
        System.out.println(NAME + ": Reading is completed successfully.");
        
        return 0;
    }
    
}
